/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "drawable/rs_property_drawable_utils.h"

#include "common/rs_optional_trace.h"
#include "platform/common/rs_log.h"
#include "property/rs_properties_painter.h"
#include "render/rs_drawing_filter.h"
#include "render/rs_kawase_blur_shader_filter.h"
#include "render/rs_linear_gradient_blur_shader_filter.h"
#include "render/rs_magnifier_shader_filter.h"
#include "render/rs_material_filter.h"
#include "render/rs_color_picker.h"

namespace Keels {
namespace Rosen {
namespace {
constexpr int TRACE_LEVEL_TWO = 2;
} // namespace
void RSPropertyDrawableUtils::DrawFilter(Drawing::Canvas* canvas, const std::shared_ptr<RSFilter>& rsFilter,
    const std::unique_ptr<RSFilterCacheManager>& cacheManager, const bool isForegroundFilter,
    bool shouldClearFilteredCache)
{
    if (!RSSystemProperties::GetBlurEnabled()) {
        ROSEN_LOGD("RSPropertyDrawableUtils::DrawFilter close blur.");
        return;
    }
    if (rsFilter == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawFilter null filter.");
        return;
    }
    if (canvas == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawFilter null canvas.");
        return;
    }

    auto filter = std::static_pointer_cast<RSDrawingFilter>(rsFilter);
    auto clipIBounds = canvas->GetDeviceClipBounds();
    RS_OPTIONAL_TRACE_NAME("DrawFilter " + rsFilter->GetDescription());
    RS_OPTIONAL_TRACE_NAME_FMT_LEVEL(TRACE_LEVEL_TWO, "DrawFilter, filterType: %d, %s, bounds: %s",
        rsFilter->GetFilterType(), rsFilter->GetDetailedDescription().c_str(), clipIBounds.ToString().c_str());
    g_blurCnt++;

    auto surface = canvas->GetSurface();
    if (surface == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawFilter surface null");
        Drawing::Brush brush = filter->GetBrush();
        Drawing::SaveLayerOps slr(nullptr, &brush, Drawing::SaveLayerOps::Flags::INIT_WITH_PREVIOUS);
        canvas->SaveLayer(slr);
        filter->PostProcess(*canvas);
        return;
    }

    auto paintFilterCanvas = static_cast<RSPaintFilterCanvas*>(canvas);
    if (paintFilterCanvas == nullptr) {
        return;
    }
    RSAutoCanvasRestore autoCanvasStore(paintFilterCanvas,
        isForegroundFilter ? RSPaintFilterCanvas::SaveType::kAlpha : RSPaintFilterCanvas::SaveType::kNone);
    if (isForegroundFilter) {
        paintFilterCanvas->SetAlpha(1.0);
    }
    auto imageClipIBounds = clipIBounds;
    std::shared_ptr<RSShaderFilter> magnifierShaderFilter = filter->GetShaderFilterWithType(RSShaderFilter::MAGNIFIER);
    if (magnifierShaderFilter != nullptr) {
        auto tmpFilter = std::static_pointer_cast<RSMagnifierShaderFilter>(magnifierShaderFilter);
        auto canvasMatrix = canvas->GetTotalMatrix();
        tmpFilter->SetMagnifierOffset(canvasMatrix);
        imageClipIBounds.Offset(tmpFilter->GetMagnifierOffsetX(), tmpFilter->GetMagnifierOffsetY());
    }

#if defined(RS_ENABLE_GL) || defined(RS_ENABLE_VK)
    // Optional use cacheManager to draw filter
    if (!paintFilterCanvas->GetDisableFilterCache() && cacheManager != nullptr && RSProperties::FilterCacheEnabled) {
        std::shared_ptr<RSShaderFilter> rsShaderFilter =
            filter->GetShaderFilterWithType(RSShaderFilter::LINEAR_GRADIENT_BLUR);
        if (rsShaderFilter != nullptr) {
            auto tmpFilter = std::static_pointer_cast<RSLinearGradientBlurShaderFilter>(rsShaderFilter);
            tmpFilter->IsOffscreenCanvas(true);
            filter->SetSnapshotOutset(false);
        }
        cacheManager->DrawFilter(*paintFilterCanvas, filter, { false, shouldClearFilteredCache });
        cacheManager->CompactFilterCache(shouldClearFilteredCache); // flag for clear witch cache after drawing
        return;
    }
#endif

    std::shared_ptr<RSShaderFilter> rsShaderFilter =
        filter->GetShaderFilterWithType(RSShaderFilter::LINEAR_GRADIENT_BLUR);
    if (rsShaderFilter != nullptr) {
        auto tmpFilter = std::static_pointer_cast<RSLinearGradientBlurShaderFilter>(rsShaderFilter);
        tmpFilter->IsOffscreenCanvas(true);
        filter->SetSnapshotOutset(false);
    }

    auto imageSnapshot = surface->GetImageSnapshot(imageClipIBounds);
    if (imageSnapshot == nullptr) {
        ROSEN_LOGD("RSPropertyDrawableUtils::DrawFilter image null");
        return;
    }
    if (RSSystemProperties::GetImageGpuResourceCacheEnable(imageSnapshot->GetWidth(), imageSnapshot->GetHeight())) {
        ROSEN_LOGD("RSPropertyDrawableUtils::DrawFilter cache image resource(w:%{public}d, h:%{public}d).",
            imageSnapshot->GetWidth(), imageSnapshot->GetHeight());
        imageSnapshot->HintCacheGpuResource();
    }

    filter->PreProcess(imageSnapshot);
    Drawing::AutoCanvasRestore acr(*canvas, true);
    canvas->ResetMatrix();
    Drawing::Rect srcRect = Drawing::Rect(0, 0, imageSnapshot->GetWidth(), imageSnapshot->GetHeight());
    Drawing::Rect dstRect = clipIBounds;
    filter->DrawImageRect(*canvas, imageSnapshot, srcRect, dstRect);
    filter->PostProcess(*canvas);
}

int RSPropertyDrawableUtils::GetAndResetBlurCnt()
{
    auto blurCnt = g_blurCnt;
    g_blurCnt = 0;
    return blurCnt;
}

void RSPropertyDrawableUtils::DrawBackgroundEffect(RSPaintFilterCanvas* canvas,
    const std::shared_ptr<RSFilter>& rsFilter, const std::unique_ptr<RSFilterCacheManager>& cacheManager,
    bool shouldClearFilteredCache)
{
    if (rsFilter == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawBackgroundEffect null filter");
        return;
    }
    if (canvas == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawBackgroundEffect null canvas");
        return;
    }
    auto surface = canvas->GetSurface();
    if (surface == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawBackgroundEffect surface null");
        return;
    }
    g_blurCnt++;
    auto clipIBounds = canvas->GetDeviceClipBounds();
    auto filter = std::static_pointer_cast<RSDrawingFilter>(rsFilter);
    RS_OPTIONAL_TRACE_NAME("RSPropertyDrawableUtils::DrawBackgroundEffect " + rsFilter->GetDescription());
    RS_OPTIONAL_TRACE_NAME_FMT_LEVEL(TRACE_LEVEL_TWO, "EffectComponent, %s, bounds: %s",
        rsFilter->GetDetailedDescription().c_str(), clipIBounds.ToString().c_str());
#if defined(RS_ENABLE_GL) || defined(RS_ENABLE_VK)
    // Optional use cacheManager to draw filter
    if (RSProperties::FilterCacheEnabled && cacheManager != nullptr && !canvas->GetDisableFilterCache() &&
        !canvas->GetHDRPresent()) {
        auto&& data = cacheManager->GeneratedCachedEffectData(*canvas, filter, clipIBounds, clipIBounds);
        cacheManager->CompactFilterCache(shouldClearFilteredCache); // flag for clear witch cache after drawing
        canvas->SetEffectData(data);
        return;
    }
#endif
    auto imageRect = clipIBounds;
    auto imageSnapshot = surface->GetImageSnapshot(imageRect);
    if (imageSnapshot == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawBackgroundEffect image snapshot null");
        return;
    }
    filter->PreProcess(imageSnapshot);
    // create a offscreen skSurface
    std::shared_ptr<Drawing::Surface> offscreenSurface =
        surface->MakeSurface(imageSnapshot->GetWidth(), imageSnapshot->GetHeight());
    if (offscreenSurface == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawBackgroundEffect offscreenSurface null");
        return;
    }
    RSPaintFilterCanvas offscreenCanvas(offscreenSurface.get());
    auto clipBounds = Drawing::Rect(0, 0, imageRect.GetWidth(), imageRect.GetHeight());
    auto imageSnapshotBounds = Drawing::Rect(0, 0, imageSnapshot->GetWidth(), imageSnapshot->GetHeight());
    filter->DrawImageRect(offscreenCanvas, imageSnapshot, imageSnapshotBounds, clipBounds);
    auto paintFilterCanvas = static_cast<RSPaintFilterCanvas*>(canvas);
    if (paintFilterCanvas != nullptr) {
        offscreenCanvas.SetHDRPresent(paintFilterCanvas->GetHDRPresent());
        offscreenCanvas.SetBrightnessRatio(paintFilterCanvas->GetBrightnessRatio());
    }
    filter->PostProcess(offscreenCanvas);

    auto imageCache = offscreenSurface->GetImageSnapshot();
    if (imageCache == nullptr) {
        ROSEN_LOGE("RSPropertyDrawableUtils::DrawBackgroundEffect imageCache snapshot null");
        return;
    }
    auto data = std::make_shared<RSPaintFilterCanvas::CachedEffectData>(std::move(imageCache), std::move(imageRect));
    canvas->SetEffectData(std::move(data));
}

void RSPropertyDrawableUtils::DrawFilterWithDRM(Drawing::Canvas* canvas, bool isDark)
{
    Drawing::Brush brush;
    int16_t rgb_light = 235;
    int16_t rgb_dark = 80;
    int16_t alpha = 245; // give a nearly opaque mask to replace blur effect
    RSColor demoColor = RSColor(rgb_light, rgb_light, rgb_light, alpha);
    if (isDark) {
        demoColor = RSColor(rgb_dark, rgb_dark, rgb_dark, alpha);
    }

    float sat = 1.0f;
    float brightness = 0.9f;
    float normalizedDegree = brightness - sat;
    const float brightnessMat[] = {
        1.0f,
        0.0f,
        0.0f,
        0.0f,
        normalizedDegree,
        0.0f,
        1.0f,
        0.0f,
        0.0f,
        normalizedDegree,
        0.0f,
        0.0f,
        1.0f,
        0.0f,
        normalizedDegree,
        0.0f,
        0.0f,
        0.0f,
        1.0f,
        0.0f,
    };
    Drawing::ColorMatrix cm;
    cm.SetSaturation(sat);
    float cmArray[Drawing::ColorMatrix::MATRIX_SIZE];
    cm.GetArray(cmArray);
    std::shared_ptr<Drawing::ColorFilter> filterCompose =
        Drawing::ColorFilter::CreateComposeColorFilter(cmArray, brightnessMat);
    auto colorImageFilter = Drawing::ImageFilter::CreateColorFilterImageFilter(*filterCompose, nullptr);
    Drawing::Filter filter;
    filter.SetImageFilter(colorImageFilter);
    brush.SetFilter(filter);
    brush.SetColor(demoColor.AsArgbInt());
    canvas->DrawBackground(brush);
}
} // namespace Rosen
} // namespace Keels
